package org.loofer.ext.utils.byteunits;

import cn.finalteam.toolsfinal.io.FileUtils;
import java.text.DecimalFormat;
import java.text.NumberFormat;

public enum BinaryByteUnit implements ByteUnit {
    BYTES {
        public long convert(long sourceCount, BinaryByteUnit sourceUnit) {
            return sourceUnit.toBytes(sourceCount);
        }

        public long toBytes(long count) {
            return count;
        }

        public long toKibibytes(long count) {
            return count / 1024;
        }

        public long toMebibytes(long count) {
            return count / 1048576;
        }

        public long toGibibytes(long count) {
            return count / 1073741824;
        }

        public long toTebibytes(long count) {
            return count / 1099511627776L;
        }

        public long toPebibytes(long count) {
            return count / 1125899906842624L;
        }
    },
    KIBIBYTES {
        public long convert(long sourceCount, BinaryByteUnit sourceUnit) {
            return sourceUnit.toKibibytes(sourceCount);
        }

        public long toBytes(long count) {
            return UnitUtil.multiply(count, 1024, 9007199254740991L);
        }

        public long toKibibytes(long count) {
            return count;
        }

        public long toMebibytes(long count) {
            return count / 1024;
        }

        public long toGibibytes(long count) {
            return count / 1048576;
        }

        public long toTebibytes(long count) {
            return count / 1073741824;
        }

        public long toPebibytes(long count) {
            return count / 1099511627776L;
        }
    },
    MEBIBYTES {
        public long convert(long sourceCount, BinaryByteUnit sourceUnit) {
            return sourceUnit.toMebibytes(sourceCount);
        }

        public long toBytes(long count) {
            return UnitUtil.multiply(count, 1048576, 8796093022207L);
        }

        public long toKibibytes(long count) {
            return UnitUtil.multiply(count, 1024, 9007199254740991L);
        }

        public long toMebibytes(long count) {
            return count;
        }

        public long toGibibytes(long count) {
            return count / 1024;
        }

        public long toTebibytes(long count) {
            return count / 1048576;
        }

        public long toPebibytes(long count) {
            return count / 1073741824;
        }
    },
    GIBIBYTES {
        public long convert(long sourceCount, BinaryByteUnit sourceUnit) {
            return sourceUnit.toGibibytes(sourceCount);
        }

        public long toBytes(long count) {
            return UnitUtil.multiply(count, 1073741824, 8589934591L);
        }

        public long toKibibytes(long count) {
            return UnitUtil.multiply(count, 1048576, 8796093022207L);
        }

        public long toMebibytes(long count) {
            return UnitUtil.multiply(count, 1024, 9007199254740991L);
        }

        public long toGibibytes(long count) {
            return count;
        }

        public long toTebibytes(long count) {
            return count / 1024;
        }

        public long toPebibytes(long count) {
            return count / 1048576;
        }
    },
    TEBIBYTES {
        public long convert(long sourceCount, BinaryByteUnit sourceUnit) {
            return sourceUnit.toTebibytes(sourceCount);
        }

        public long toBytes(long count) {
            return UnitUtil.multiply(count, 1099511627776L, 8388607);
        }

        public long toKibibytes(long count) {
            return UnitUtil.multiply(count, 1073741824, 8589934591L);
        }

        public long toMebibytes(long count) {
            return UnitUtil.multiply(count, 1048576, 8796093022207L);
        }

        public long toGibibytes(long count) {
            return UnitUtil.multiply(count, 1024, 9007199254740991L);
        }

        public long toTebibytes(long count) {
            return count;
        }

        public long toPebibytes(long count) {
            return count / 1024;
        }
    },
    PEBIBYTES {
        public long convert(long sourceCount, BinaryByteUnit sourceUnit) {
            return sourceUnit.toPebibytes(sourceCount);
        }

        public long toBytes(long count) {
            return UnitUtil.multiply(count, 1125899906842624L, 8191);
        }

        public long toKibibytes(long count) {
            return UnitUtil.multiply(count, 1099511627776L, 8388607);
        }

        public long toMebibytes(long count) {
            return UnitUtil.multiply(count, 1073741824, 8589934591L);
        }

        public long toGibibytes(long count) {
            return UnitUtil.multiply(count, 1048576, 8796093022207L);
        }

        public long toTebibytes(long count) {
            return UnitUtil.multiply(count, 1024, 9007199254740991L);
        }

        public long toPebibytes(long count) {
            return count;
        }
    };
    
    private static final long B = 1;
    private static final long GB = 1073741824;
    private static final long KB = 1024;
    private static final long MAX = Long.MAX_VALUE;
    private static final long MB = 1048576;
    private static final long PB = 1125899906842624L;
    private static final long TB = 1099511627776L;
    private static final String[] UNITS = null;

    static {
        UNITS = new String[]{"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
    }

    public static String format(long bytes) {
        return format(bytes, new DecimalFormat("#,##0.#"));
    }

    public static String format(long bytes, String pattern) {
        return format(bytes, new DecimalFormat(pattern));
    }

    public static String format(long bytes, NumberFormat format) {
        if (bytes < 0) {
            throw new IllegalArgumentException("bytes < 0: " + bytes);
        }
        int unitIndex = 0;
        double count = (double) bytes;
        while (count >= FileUtils.KB && unitIndex < UNITS.length - 1) {
            count /= FileUtils.KB;
            unitIndex++;
        }
        return format.format(count) + ' ' + UNITS[unitIndex];
    }

    public long convert(long sourceCount, BinaryByteUnit sourceUnit) {
        throw new AbstractMethodError();
    }

    public long toKibibytes(long count) {
        throw new AbstractMethodError();
    }

    public long toMebibytes(long count) {
        throw new AbstractMethodError();
    }

    public long toGibibytes(long count) {
        throw new AbstractMethodError();
    }

    public long toTebibytes(long count) {
        throw new AbstractMethodError();
    }

    public long toPebibytes(long count) {
        throw new AbstractMethodError();
    }
}
